package com.codi.bus.core.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.codi.base.cache.CacheUtil;
import com.codi.base.util.EqualsUtil;
import com.codi.base.util.ListUtil;
import com.codi.base.util.MoneyUtils;
import com.codi.base.util.StringUtil;
import com.codi.bus.constant.CacheKeyConst;
import com.codi.bus.constant.GlobalConstant;
import com.codi.bus.constant.RuleConstant;
import com.codi.bus.core.dao.LookupDao;
import com.codi.bus.core.domain.Lookup;
import com.codi.bus.core.resultModel.BaseResult;
import com.codi.bus.core.resultModel.FundNetValueResult;
import com.codi.bus.core.service.FundDetailService;
import com.codi.bus.core.service.QueryService;
import com.codi.bus.core.service.T0Service;
import com.codi.bus.core.service.model.T0ConfigModel;
import com.codi.bus.core.service.model.T0Model;
import com.codi.bus.core.service.model.TradeLimitModel;
import com.codi.bus.util.LookupUtil;
import com.codi.message.constant.ErrorConst;
import com.codi.message.util.MessageUtil;
import com.google.common.base.Strings;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * T0 service
 *
 * @author shi.pengyan
 * @version 1.0 2017-10-20 10:54
 * @since 1.0
 */
@Slf4j
@Service
@SuppressWarnings("Duplicates")
public class T0ServiceImpl implements T0Service {


    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private LookupDao lookupDao;

    @Autowired
    private LookupServiceImpl lookupService;

    @Autowired
    private FundDetailService fundDetailService;

    @Autowired
    private QueryService queryService;


    private static final String NAME_SPACE = "T0";

    @Override
    @Transactional(readOnly = true)
    public T0Model queryTopRateFund(Boolean isOrg) {
        String key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_TOP_RATE_FUND_INFO, isOrg ? "org" : "user");

        // from cache
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String resultStr = valueOperations.get(key);

        T0Model result;

        if (StringUtil.isNotEmpty(resultStr)) {
            try {
                result = JSONObject.parseObject(resultStr, T0Model.class);
                return result;
            } catch (Exception e) {
                log.error("fail to parse obj", e);
            }
        }

        // calc
        result = calcTopRateFund(isOrg);
        valueOperations.set(key, JSONObject.toJSONString(result));

        return result;
    }


    @Override
    @Transactional(readOnly = true)
    public T0Model calcTopRateFund(Boolean isOrg) {
        List<T0Model> funds = calcT0Funds(isOrg);

        return funds.get(0);
    }

    @Override
    @Transactional(readOnly = true)
    public List<T0Model> queryT0Funds(Boolean isOrg) {
        String key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_FUNDS_DESC, isOrg ? "org" : "user");


        // from cache
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String resultStr = valueOperations.get(key);


        List<T0Model> result;


        if (StringUtil.isNotEmpty(resultStr)) {

            try {
                result = JSONArray.parseArray(resultStr, T0Model.class);
                return result;
            } catch (Exception e) {
                log.error("fail to parse array", e);
            }
        }

        result = calcT0Funds(isOrg);
        valueOperations.set(key, JSONArray.toJSONString(result));

        return result;
    }

    /**
     * 获取T0基金 trade_limits
     * @param
     * @return
     */
    @Transactional(readOnly = true)
    public T0Model getT0Model(String fundCode) {
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);

        T0Model model = new T0Model();
        List<TradeLimitModel> tradeLimitModels = new ArrayList<>();

        if (lookups != null) {
            for (Lookup lookup : lookups) {
                if (fundCode.trim().equals(lookup.getRuleValue().trim())) {
                    String ruleLimitValue = lookup.getRuleLimitValue();

                    if (LookupUtil.isJSONObject(ruleLimitValue.trim())) {
                        JSONObject object = JSONObject.parseObject(ruleLimitValue);

                        if (object != null) {
                            JSONObject user = object.getJSONObject("user");
                            JSONObject org = object.getJSONObject("org");

                            if (user != null) {
                                String minMoney = user.getString("minMoney");
                                TradeLimitModel tradeLimitModel = new TradeLimitModel();
                                tradeLimitModel.setMinMoney(minMoney);
                                tradeLimitModel.setFundCode(fundCode);
                                tradeLimitModel.setUserOrOrg("user");
                                tradeLimitModel.setReminder(user.getString("reminder"));

                                tradeLimitModels.add(tradeLimitModel);
                            }

                            if (org != null) {
                                String minMoney = org.getString("minMoney");
                                TradeLimitModel tradeLimitModel = new TradeLimitModel();
                                tradeLimitModel.setMinMoney(minMoney);
                                tradeLimitModel.setFundCode(fundCode);
                                tradeLimitModel.setUserOrOrg("org");
                                tradeLimitModel.setReminder(org.getString("reminder"));

                                tradeLimitModels.add(tradeLimitModel);
                            }
                        }
                    }

                    model.setLimits(tradeLimitModels);

                    return model;
                }
            }
        }

        return null;
    }

    @Override
    @Transactional(readOnly = true)
    public List<T0Model> calcT0Funds(Boolean isOrg) {
        String custType = GlobalConstant.CUST_TYPE_PERSONAL;
        String capitalMode = null;
        if (isOrg) {
            custType = GlobalConstant.CUST_TYPE_ORGANIZATION;
            capitalMode = GlobalConstant.CAPTIAL_MODE_K;
        }


        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);

        List<String> fundCodes = new ArrayList<>();
        Map<String, String> nameMap = new HashMap<>();
        Map<String, String> reportMap = new HashMap<>();
        for (int i = 0, j = lookups.size(); i < j; i++) {
            Lookup lookup = lookups.get(i);
            if (lookup.getRuleText().toUpperCase().endsWith("A")||lookup.getRuleText().toUpperCase().endsWith("E")) {
                fundCodes.add(lookup.getRuleValue());
                nameMap.put(lookup.getRuleValue(), lookup.getRuleText());
                reportMap.put(lookup.getRuleValue(),lookup.getRuleCreateUserId());
            }
        }

        log.info("fundCodes=[{}]", fundCodes);

        Date now = new Date();

        List<T0Model> results = new ArrayList<>(fundCodes.size());


        for (int i = 0, j = fundCodes.size(); i < j; i++) {
            FundNetValueResult netValueResult = fundDetailService.getFundNetValue(fundCodes.get(i), now);

            T0Model model = new T0Model();

            model.setFundCode(netValueResult.getFundCode());
            model.setFundName(nameMap.get(model.getFundCode()));
            model.setTradingDay(netValueResult.getTradingDay());
            model.setFundReport(reportMap.get(model.getFundCode()));

            // 每万份基金单位当日收益（元）
            model.setDailyProfit(netValueResult.getDailyProfit());
            model.setDailyProfitStr(MoneyUtils.formatMoney(netValueResult.getDailyProfit(), 4));

            // 最近7日折算年收益率（%）
            model.setLatestWeeklyYield(netValueResult.getLatestWeeklyYield());
            model.setLatestWeeklyYieldStr(MoneyUtils.formatMoneyPercentNoRund(netValueResult.getLatestWeeklyYield(), 2));

            model.setLimits(queryService.queryTradeLimits(fundCodes.get(i), capitalMode, null, custType));

            results.add(model);
        }


        // sort
        Collections.sort(results, new Comparator<T0Model>() {
            @Override
            public int compare(T0Model o1, T0Model o2) {
                return o2.getLatestWeeklyYield().compareTo(o1.getLatestWeeklyYield());
            }
        });

        return results;
    }


    @Override
    @Transactional(readOnly = true)
    public T0Model queryT0Fund(Boolean isOrg, String fundCode) {

        String custType = GlobalConstant.CUST_TYPE_PERSONAL;
        String capitalMode = null;
        if (isOrg) {
            custType = GlobalConstant.CUST_TYPE_ORGANIZATION;
            capitalMode = GlobalConstant.CAPTIAL_MODE_K;
        }

        FundNetValueResult netValueResult = fundDetailService.getFundNetValue(fundCode, new Date());

        T0Model model = new T0Model();

        model.setFundCode(netValueResult.getFundCode());
        model.setFundName(netValueResult.getChiNameAbbr());
        model.setTradingDay(netValueResult.getTradingDay());


        // 每万份基金单位当日收益（元）
        model.setDailyProfit(netValueResult.getDailyProfit());
        model.setDailyProfitStr(MoneyUtils.formatMoney(netValueResult.getDailyProfit(), 4));

        // 最近7日折算年收益率（%）
        model.setLatestWeeklyYield(netValueResult.getLatestWeeklyYield());
        model.setLatestWeeklyYieldStr(MoneyUtils.formatMoneyPercentNoRund(netValueResult.getLatestWeeklyYield(), 2));


        model.setLimits(queryService.queryTradeLimits(fundCode, capitalMode, null, custType));

        return model;
    }

    @Override
    public List<T0Model> queryT0Funds(Boolean isOrg, List<String> fundCodes) {
        String custType = GlobalConstant.CUST_TYPE_PERSONAL;
        String capitalMode = null;
        if (isOrg) {
            custType = GlobalConstant.CUST_TYPE_ORGANIZATION;
            capitalMode = GlobalConstant.CAPTIAL_MODE_K;
        }

        log.info("fundCodes=[{}]", fundCodes);

        Date now = new Date();

        List<T0Model> results = new ArrayList<>(fundCodes.size());


        for (int i = 0, j = fundCodes.size(); i < j; i++) {
            FundNetValueResult netValueResult = fundDetailService.getFundNetValue(fundCodes.get(i), now);

            T0Model model = new T0Model();

            model.setFundCode(netValueResult.getFundCode());
            model.setFundName(netValueResult.getSecuAbbr());
            model.setTradingDay(netValueResult.getTradingDay());


            // 每万份基金单位当日收益（元）
            model.setDailyProfit(netValueResult.getDailyProfit());
            model.setDailyProfitStr(MoneyUtils.formatMoney(netValueResult.getDailyProfit(), 4));

            // 最近7日折算年收益率（%）
            model.setLatestWeeklyYield(netValueResult.getLatestWeeklyYield());
            model.setLatestWeeklyYieldStr(MoneyUtils.formatMoney(netValueResult.getLatestWeeklyYield().multiply(GlobalConstant.BIG_DECIMAL_100), 2));


            model.setLimits(queryService.queryTradeLimits(fundCodes.get(i), capitalMode, null, custType));

            results.add(model);
        }


        // sort
        Collections.sort(results, new Comparator<T0Model>() {
            @Override
            public int compare(T0Model o1, T0Model o2) {
                return o2.getLatestWeeklyYield().compareTo(o1.getLatestWeeklyYield());
            }
        });

        return results;
    }

    @Override
    public void reloadCache() {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String ORG = "org", USER = "user";
        String key;

        List<T0Model> result;
        T0Model model;


        // 个人
        key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_FUNDS_DESC, USER);
        result = calcT0Funds(false);
        log.info("key={},value={}", key, result);
        valueOperations.set(key, JSONArray.toJSONString(result));

        // 个人 top
        key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_TOP_RATE_FUND_INFO, USER);
        model = result.get(0);
        log.info("key={},value={}", key, model);
        valueOperations.set(key, JSONObject.toJSONString(model));


        // 机构
        key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_FUNDS_DESC, ORG);
        result = calcT0Funds(true);
        log.info("key={},value={}", key, result);
        valueOperations.set(key, JSONArray.toJSONString(result));

        // 机构top
        key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_TOP_RATE_FUND_INFO, ORG);
        model = result.get(0);
        log.info("key={},value={}", key, model);
        valueOperations.set(key, JSONObject.toJSONString(model));
    }

    @Override
    @Transactional
    public BaseResult configAppHomeConfig(String t0FundCode) {
        BaseResult result = new BaseResult(false);

        Lookup lookup = new Lookup();
        lookup.setRuleType(RuleConstant.APP_HOME_DISPLAY_T0);

        if ("-1".equalsIgnoreCase(t0FundCode)) {
            // 取消，自定义
            lookup.setRuleStatus(0);
        } else {
            // 设置

            //validate is t0 fund A
            boolean isT0Fund = lookupService.isT0Fund(t0FundCode);
            if (isT0Fund) {
                lookup.setRuleStatus(1);
                lookup.setRuleValue(t0FundCode);
            } else {
                result.setErrorCode(ErrorConst.T0_FUND_CODE_ERROR);
                result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.T0_FUND_CODE_ERROR));

                return result;
            }
        }
        lookupDao.updateByRuleType(lookup);

        result.setSuccess(true);

        return result;
    }

    @Override
    public BaseResult configT0List(boolean isOrg) {
        T0ConfigModel model = new T0ConfigModel();

        List<T0Model> data = queryT0Funds(isOrg);

        /**
         * 需要后台设置 最低起购金融 与 提示语
         */
//        if (data != null) {
//            data.forEach(item -> {
//                item.setLimits(null);
//            });
//        }

        model.setT0Models(data);

        Lookup lookup = lookupService.getLookup(RuleConstant.APP_HOME_DISPLAY_T0);
        if (EqualsUtil.equals(lookup.getRuleStatus().intValue(), 1)) {
            model.setActiveFundCode(lookup.getRuleValue());
        }

        BaseResult ret = new BaseResult(true);
        ret.setResult(model);
        return ret;
    }

    @Override
    public BaseResult getTOFundTradeLimit(String fundCode) {
        BaseResult result = new BaseResult();

        T0Model t0Model = getT0Model(fundCode);

        result.setResult(t0Model);

        return result;
    }

    @Override
    public BaseResult setT0FundTradeLimit(String fundCode, String minMoney, String userOrOrg, String reminder) {
        Lookup lookup = lookupService.getLookup(RuleConstant.RULE_LOOKUP_TYPE_T0FUND, fundCode.trim());
        BaseResult result = new BaseResult();

        if (lookup != null) {
            String ruleLimitValue = lookup.getRuleLimitValue();

            if (LookupUtil.isJSONObject(ruleLimitValue)) {
                JSONObject jsonObject = JSONObject.parseObject(ruleLimitValue);

                JSONObject jsonObject1 = jsonObject.getJSONObject(userOrOrg);

                if (jsonObject1 == null) {
                    jsonObject1 = new JSONObject();
                    jsonObject1.put("minMoney", minMoney);
                    jsonObject1.put("reminder", reminder);

                    jsonObject.put(userOrOrg, jsonObject1);
                } else {
                    jsonObject1.put("minMoney", minMoney);
                    jsonObject1.put("reminder", reminder);
                }

                ruleLimitValue = JSONObject.toJSONString(jsonObject);

                lookup.setRuleLimitValue(ruleLimitValue);

                lookupService.updateLookup(lookup);
            }

            if ("".equals(ruleLimitValue)) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("minMoney", minMoney);
                jsonObject.put("reminder", reminder);
                JSONObject father = new JSONObject();
                father.put(userOrOrg, jsonObject);

                ruleLimitValue = JSONObject.toJSONString(father);
                lookup.setRuleLimitValue(ruleLimitValue);

                lookupService.updateLookup(lookup);
            }
        }

        String key = CacheUtil.getKey(NAME_SPACE, CacheKeyConst.T0_FUNDS_DESC, userOrOrg);


        // from cache
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String resultStr = valueOperations.get(key);


        List<T0Model> t0Models;


        if (StringUtil.isNotEmpty(resultStr)) {

            try {
                t0Models = JSONArray.parseArray(resultStr, T0Model.class);

                for (T0Model t0Model : t0Models) {
                    if (fundCode.equals(t0Model.getFundCode())) {
                        List<TradeLimitModel> limitModels = t0Model.getLimits();

                        if (! ListUtil.isEmpty(limitModels)) {
                            limitModels.forEach(item -> {
                                if (Strings.isNullOrEmpty(minMoney)) {
                                    item.setMinValue(new BigDecimal("0")).setReminder(reminder);
                                } else {
                                    item.setMinValue(new BigDecimal(minMoney)).setReminder(reminder);
                                }

                            });
                        } else {
                            limitModels = new ArrayList<>();
                            TradeLimitModel tradeLimitModel = new TradeLimitModel();

                            if (Strings.isNullOrEmpty(minMoney)) {
                                tradeLimitModel.setMinValue(new BigDecimal("0"));
                            } else {
                                tradeLimitModel.setMinValue(new BigDecimal(minMoney));
                            }

                            tradeLimitModel.setReminder(reminder);
                            tradeLimitModel.setUserOrOrg(userOrOrg);

                            limitModels.add(tradeLimitModel);

                            t0Model.setLimits(limitModels);
                        }
                    }
                }

                valueOperations.set(key, JSONObject.toJSONString(t0Models));

                System.out.println(JSONObject.toJSON(t0Models));


            } catch (Exception e) {
                log.error("fail to parse array", e);
            }
        }

        return result;
    }
}
